/**
 * @file TcpApp_client_misc.c
 * @brief Helper functions for TcpApp/TCP client
 *
 * @section License
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Copyright (C) 2010-2025 Oryx Embedded SARL. All rights reserved.
 *
 * This file is part of CycloneTCP Open.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 2.5.2
 **/

// Switch to the appropriate trace level
#define TRACE_LEVEL TCP_APP_TRACE_LEVEL

// Dependencies
#include "client/tcp_client_app.h"
#include "client/tcp_client_app_pdu.h"
#include "client/tcp_client_app_transport.h"
#include "client/tcp_client_app_misc.h"
#include "debug.h"

/**
 * @brief Perform TcpApp transaction
 * @param[in] context Pointer to the TcpApp/TCP client context
 * @return Error code
 **/

error_t TcpAppClientTransaction(TcpAppClientContext *context)
{
   error_t error;
   size_t n;
   systime_t time;
   uint8_t *pdu;

   // Initialize status code
   error = NO_ERROR;

   // Get current time
   time = osGetSystemTime();

   // Adjust timeout
   if (timeCompare(context->timestamp + context->timeout, time) > 0)
   {
      socketSetTimeout(context->socket, context->timestamp +
                                            context->timeout - time);
   }
   else
   {
      socketSetTimeout(context->socket, 0);
   }

   // Check current state
   if (context->state == TCP_APP_CLIENT_STATE_SENDING)
   {
      // Send TcpApp request
      if (context->requestAduPos < context->requestAduLen)
      {
         // Send more data
         error = TcpAppClientSendData(context,
                                      context->requestAdu + context->requestAduPos,
                                      context->requestAduLen - context->requestAduPos,
                                      &n, SOCKET_FLAG_NO_DELAY);

         // Check status code
         if (error == NO_ERROR || error == ERROR_TIMEOUT)
         {
            // Advance data pointer
            context->requestAduPos += n;
         }
      }
      else
      {
         // Flush receive buffer
         context->responseAduLen = 0;
         context->responseAduPos = 0;
         // Wait for response ADU
         context->state = TCP_APP_CLIENT_STATE_RECEIVING;
      }
   }
   else if (context->state == TCP_APP_CLIENT_STATE_RECEIVING)
   {
      // Receive TcpApp response
      if (context->responseAduPos == 0)
      {
         // Receive more data
         error = TcpAppClientReceiveData(context,
                                         context->responseAdu + context->responseAduPos,
                                         sizeof(context->responseAdu) - context->responseAduPos, &n, 0);

         ///////////////////// edit for app//////////////////////////
         context->responseAduLen = n;

         // Check status code
         if (error == NO_ERROR)
         {
            // Advance data pointer
            context->responseAduPos += n;

            // MBAP header successfully received?
            if (context->responseAduPos >= 0)
            {
               context->responseAduLen = n;
               // Successful processing
               error = NO_ERROR;
            }
         }

         error = NO_ERROR;
         context->state = TCP_APP_CLIENT_STATE_COMPLETE;
      }
      else
      {
         // Check whether the received response matches the request
         error = NO_ERROR;
         context->state = TCP_APP_CLIENT_STATE_COMPLETE;
      }
   }
   else
   {
      // Report an error
      error = ERROR_WRONG_STATE;
   }

   // Check status code
   if (error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
   {
      // Check whether the timeout has elapsed
      error = TcpAppClientCheckTimeout(context);
   }

   // TcpApp transaction failed?
   if (error != NO_ERROR && error != ERROR_WOULD_BLOCK)
   {
      // Revert to default state
      context->state = TCP_APP_CLIENT_STATE_CONNECTED;
   }

   // Return status code
   return error;
}

/**
 * @brief Determine whether a timeout error has occurred
 * @param[in] context Pointer to the TcpApp/TCP client context
 * @return Error code
 **/

error_t TcpAppClientCheckTimeout(TcpAppClientContext *context)
{
#if (NET_RTOS_SUPPORT == DISABLED)
   error_t error;
   systime_t time;

   // Get current time
   time = osGetSystemTime();

   // Check whether the timeout has elapsed
   if (timeCompare(time, context->timestamp + context->timeout) >= 0)
   {
      // Report a timeout error
      error = ERROR_TIMEOUT;
   }
   else
   {
      // The operation would block
      error = ERROR_WOULD_BLOCK;
   }

   // Return status code
   return error;
#else
   // Report a timeout error
   return ERROR_TIMEOUT;
#endif
}
